#include "common.h"

#include "utils/systime.h"

#include "Vaxi_mem.h"
#include "Vaxi_mem__Dpi.h"


#ifdef __cplusplus
extern "C" {
#endif

    uint64_t mem[0x1000000] ={0};
    // DPI IMPORTS
    // DPI import at /home/qingchen/Project/ysyx-workbench/npc/vsrc/Cache/axi_mem.v:1:33
    long long mem_read(long long raddr, svBit is_icache) {
        return mem[raddr/sizeof(uint64_t)];
    }
    // DPI import at /home/qingchen/Project/ysyx-workbench/npc/vsrc/Cache/axi_mem.v:2:30
    void mem_write(long long waddr, long long wdata, char wmask) {
        uint64_t old = mem[waddr/sizeof(uint64_t)];
        uint64_t temp = 0;
        for (int i = 0; i < sizeof(uint64_t); i++)
        {
            if (BITS(wmask, i, i))
            {
                temp |= BITS(wdata, i * 8 + 7, i * 8) << i * 8;
            }
            else
            {
                temp |= BITS(old, i * 8 + 7, i * 8) << i * 8;
            }
            
        }
        mem[waddr/sizeof(uint64_t)] = temp;
    }
    // DPI import at /home/qingchen/Project/ysyx-workbench/npc/vsrc/top.v:1:33
    long long pmem_read(long long raddr, char rsize, char is_icache){return -1;}
    // DPI import at /home/qingchen/Project/ysyx-workbench/npc/vsrc/top.v:2:30
    void pmem_write(long long waddr, long long wdata, char wmask){}
    void raise_hard_intr() {}
    void submit_inst(long long pc, int inst, long long next_pc) {}
    void difftest_memory_access(long long addr) {}

#ifdef __cplusplus
}
#endif
Vaxi_mem *top;
void quit(int t) 
{
    delete top;
    exit(t);
}
#define TEST_CNT 1024000
#define SIM_MAX_CYCLES (TEST_CNT*20)
static uint32_t total_cycles = 0;
void simcycle(uint32_t n)
{
    total_cycles += n;

    while (n--)
    {
        top->ACLK = 0;
        top->eval();
        top->ACLK = 1;
        top->eval();
    }
    if(total_cycles > SIM_MAX_CYCLES) quit(-1);
}
void reset()
{
    top->ACLK = 0;
    top->ARESETn = 0;
    top->RREADY = 1;
    top->BREADY = 1;
    top->eval();
    simcycle(10);
    top->ARESETn = 1;

}
uint64_t axi_read(uint64_t addr) {
    top->RREADY = 1;
    top->ARADDR = addr;
    top->ARVALID = 1;
    while(top->ARREADY == 0) simcycle(1);
    simcycle(1);
    top->ARADDR = 0;
    top->ARVALID = 0;
    while(top->RVALID == 0){
        simcycle(1);
    }
    return top->RDATA;
}
void axi_write(uint64_t addr,uint64_t data,uint8_t wmask) {
    /* Write the address channel. */
    top->AWADDR = addr;
    top->AWVALID = 1;
    while(top->AWREADY == 0) {
        simcycle(1);
    }
    /* Take one cycles to write address. */
    simcycle(1);

    /* Write the data channel. */
    top->WSTRB = wmask;
    top->WDATA = data;
    top->WVALID = 1;
    while(top->AWREADY == 0) {
        simcycle(1);
    }
    /* Take one cycles to write data. */
    simcycle(1);

    /* Wait the response channel valid. */
    while (top->BVALID == 0)
    {
        simcycle(1);
    }
    
}
uint64_t test[TEST_CNT];
int main(int argc, char **argv, char **env)
{
    top = new Vaxi_mem;
    srand(time(NULL));
    for (int i = 0; i < TEST_CNT; i++)
    {
        test[i] = rand();

    }
    
    printf("sim start\n");
    uint64_t start_tick,end_tick;
    start_tick = SYS_GetTicks();
    reset();
    printf("Write test data to memory.\n");
    for (int i = 0; i < TEST_CNT; i++)
    {
        axi_write(i*sizeof(uint64_t),test[i],0xff);
    }
    printf("Read the test data from memory.\n");
    for (int i = 0; i < TEST_CNT; i++)
    {
        uint64_t data = axi_read(i*sizeof(uint64_t));
        if(data != test[i]) {
            printf("error occur in %d, read data: 0x%016lx, test data: 0x%016lx memory data: 0x%016lx\n",i,data,test[i],mem[i]);
            break;
        } 
    }
    
    
    end_tick = SYS_GetTicks();
    uint64_t total_us = end_tick-start_tick;
    printf("sim end\n");
    printf("sim time: %dms, total_cycles:%d, speed:%.0lf Cycles/s\n",total_us/1000,total_cycles,total_cycles*1000000.0/total_us);
    delete top;
    return 0;
}